namespace TXLooker.WorkItemExtention.WIQL
{
    using System;
    using System.Collections;

    internal class Scanner
    {
        private int m_pos;
        private ArrayList m_tokens;

        public Scanner(ArrayList tokens)
        {
            this.m_tokens = tokens;
        }

        public void CheckTail()
        {
            int pos = this.m_pos;
            NodeItem item = this.NextToken();
            if ((item != null) && ((item.NodeType != NodeType.Operation) || (item.Value.Length != 0)))
            {
                this.m_pos = pos;
                this.ThrowSyntaxError(SyntaxError.ExpectingEndOfString);
            }
        }

        private NodeItem EnsureToken(NodeType type, string str, SyntaxError err)
        {
            NodeItem item = this.ScanToken(type, str);
            if (item == null)
            {
                this.ThrowSyntaxError(err);
            }
            return item;
        }

        private NodeItem NextToken()
        {
            return ((this.m_pos < this.m_tokens.Count) ? ((NodeItem) this.m_tokens[this.m_pos++]) : null);
        }

        public NodeSelect Scan()
        {
            return this.ScanSelect();
        }

        private Node ScanCondition1(NodeTableName table)
        {
            Node node = this.ScanToken(NodeType.Operation, "(");
            if (node != null)
            {
                Node node2 = this.ScanWhere(table);
                Node node3 = this.EnsureToken(NodeType.Operation, ")", SyntaxError.ExpectingLeftBracket);
                node2.StartOffset = node.StartOffset;
                node2.EndOffset = node3.EndOffset;
                return node2;
            }
            NodeCondition condition = new NodeCondition();
            Node node4 = condition;
            condition.Left = this.ScanFieldName(table);
            Stack stack = new Stack();
            while (true)
            {
                while (this.ScanToken(NodeType.Name, "not") != null)
                {
                    stack.Push(NodeType.Not);
                }
                if (this.ScanToken(NodeType.Name, "ever") != null)
                {
                    stack.Push(NodeType.Ever);
                }
                else
                {
                    if (this.ScanToken(NodeType.Name, "never") == null)
                    {
                        goto Label_00F4;
                    }
                    stack.Push(NodeType.Not);
                    stack.Push(NodeType.Ever);
                }
            }
        Label_00C6:
            switch (((NodeType) stack.Pop()))
            {
                case NodeType.Not:
                    node4 = new NodeNotOperator(node4);
                    goto Label_00F4;

                case NodeType.Ever:
                    node4 = new NodeEverOperator(node4);
                    goto Label_00F4;
            }
        Label_00F4:
            if (stack.Count > 0)
            {
                goto Label_00C6;
            }
            condition.Condition = this.ScanConditionOperator(node4 != condition);
            if (condition.Condition == Condition.None)
            {
                this.ThrowSyntaxError(SyntaxError.ExpectingComparisonOperator);
            }
            if (condition.Condition == Condition.In)
            {
                Node node5 = this.EnsureToken(NodeType.Operation, "(", SyntaxError.ExpectingLeftBracket);
                Node node6 = this.ScanValueList(table);
                Node node7 = this.EnsureToken(NodeType.Operation, ")", SyntaxError.ExpectingRightBracket);
                node6.StartOffset = node5.StartOffset;
                node6.EndOffset = node7.EndOffset;
                condition.Right = node6;
            }
            else
            {
                condition.Right = this.ScanValue(table);
            }
            node4.StartOffset = condition.Left.StartOffset;
            node4.EndOffset = condition.Right.EndOffset;
            return node4;
        }

        private Node ScanCondition2(NodeTableName table)
        {
            Node node = this.ScanToken(NodeType.Name, "not");
            if (node == null)
            {
                node = this.ScanToken(NodeType.Operation, "!");
            }
            if (node != null)
            {
                Node node2 = this.ScanCondition2(table);
                Node node3 = new NodeNotOperator(node2);
                node3.StartOffset = node.StartOffset;
                node3.EndOffset = node2.EndOffset;
                return node3;
            }
            node = this.ScanToken(NodeType.Name, "ever");
            if (node != null)
            {
                Node node4 = this.ScanCondition2(table);
                Node node5 = new NodeEverOperator(node4);
                node5.StartOffset = node.StartOffset;
                node5.EndOffset = node4.EndOffset;
                return node5;
            }
            node = this.ScanToken(NodeType.Name, "never");
            if (node != null)
            {
                Node node6 = this.ScanCondition2(table);
                Node node7 = new NodeNotOperator(new NodeEverOperator(node6));
                node7.StartOffset = node.StartOffset;
                node7.EndOffset = node6.EndOffset;
                return node7;
            }
            return this.ScanCondition1(table);
        }

        private Node ScanCondition3(NodeTableName table)
        {
            Node node = this.ScanCondition2(table);
            NodeAndOperator @operator = null;
            while (true)
            {
                Node node2 = this.ScanToken(NodeType.Name, "and");
                if (node2 == null)
                {
                    node2 = this.ScanToken(NodeType.Operation, "&&");
                }
                if (node2 == null)
                {
                    break;
                }
                if (@operator == null)
                {
                    @operator = new NodeAndOperator();
                    @operator.Add(node);
                }
                @operator.Add(this.ScanCondition2(table));
            }
            if (@operator != null)
            {
                @operator.StartOffset = @operator[0].StartOffset;
                @operator.EndOffset = @operator[@operator.Count - 1].EndOffset;
                node = @operator;
            }
            return node;
        }

        private Node ScanCondition4(NodeTableName table)
        {
            Node node = this.ScanCondition3(table);
            NodeOrOperator @operator = null;
            while (true)
            {
                Node node2 = this.ScanToken(NodeType.Name, "or");
                if (node2 == null)
                {
                    node2 = this.ScanToken(NodeType.Operation, "||");
                }
                if (node2 == null)
                {
                    break;
                }
                if (@operator == null)
                {
                    @operator = new NodeOrOperator();
                    @operator.Add(node);
                }
                @operator.Add(this.ScanCondition3(table));
            }
            if (@operator != null)
            {
                @operator.StartOffset = @operator[0].StartOffset;
                @operator.EndOffset = @operator[@operator.Count - 1].EndOffset;
                node = @operator;
            }
            return node;
        }

        private Condition ScanConditionOperator(bool afterNotOrEver)
        {
            int pos = this.m_pos;
            NodeItem item = this.NextToken();
            if ((item != null) && ((item.NodeType == NodeType.Name) || ((item.NodeType == NodeType.Operation) && !afterNotOrEver)))
            {
                Condition condition = ConditionalOperators.Find(item.Value);
                if (condition != Condition.None)
                {
                    return condition;
                }
            }
            this.m_pos = pos;
            if (!afterNotOrEver)
            {
                return Condition.None;
            }
            return Condition.Equals;
        }

        private Node ScanExpression(NodeTableName table)
        {
            int pos;
            Node node = this.ScanSingleValue(table);
        Label_0008:
            pos = this.m_pos;
            NodeItem item = this.NextToken();
            if (item != null)
            {
                Arithmetic none = Arithmetic.None;
                Node node2 = null;
                if (item.NodeType == NodeType.Operation)
                {
                    none = ArithmeticalOperators.Find(item.Value);
                    if (none != Arithmetic.None)
                    {
                        node2 = this.ScanSingleValue(table);
                    }
                }
                else if (item.NodeType == NodeType.Number)
                {
                    none = ArithmeticalOperators.Find(item.Value.Substring(0, 1));
                    if (none != Arithmetic.None)
                    {
                        item.Value = item.Value.Substring(1);
                        node2 = item;
                    }
                }
                if (none != Arithmetic.None)
                {
                    NodeArithmetic arithmetic2 = new NodeArithmetic();
                    arithmetic2.Arithmetic = none;
                    arithmetic2.Left = node;
                    arithmetic2.Right = node2;
                    arithmetic2.StartOffset = arithmetic2.Left.StartOffset;
                    arithmetic2.EndOffset = arithmetic2.Right.EndOffset;
                    node = arithmetic2;
                    goto Label_0008;
                }
            }
            this.m_pos = pos;
            return node;
        }

        private NodeFieldName ScanFieldName(NodeTableName table)
        {
            int pos = this.m_pos;
            NodeItem item = this.NextToken();
            if ((item != null) && (item.NodeType == NodeType.Name))
            {
                return new NodeFieldName((NodeName) item);
            }
            this.m_pos = pos;
            this.ThrowSyntaxError(SyntaxError.ExpectingFieldName);
            return null;
        }

        private NodeFieldList ScanFieldNameList(bool direction, NodeType nodeType)
        {
            NodeFieldList list = new NodeFieldList(nodeType);
            do
            {
                NodeFieldName node = this.ScanFieldName(null);
                if (direction)
                {
                    if (this.ScanToken(NodeType.Name, "asc") != null)
                    {
                        node.Direction = Direction.Ascending;
                    }
                    else if (this.ScanToken(NodeType.Name, "desc") != null)
                    {
                        node.Direction = Direction.Descending;
                    }
                }
                list.Add(node);
            }
            while (this.ScanToken(NodeType.Operation, ",") != null);
            if (list.Count != 0)
            {
                list.StartOffset = list[0].StartOffset;
                list.EndOffset = list[list.Count - 1].EndOffset;
            }
            return list;
        }

        private NodeSelect ScanSelect()
        {
            Node node = this.EnsureToken(NodeType.Name, "select", SyntaxError.ExpectingSelect);
            NodeSelect select = new NodeSelect();
            select.Fields = this.ScanFieldNameList(false, NodeType.FieldList);
        Label_0023:
            while (this.ScanToken(NodeType.Name, "from") != null)
            {
                if (select.From != null)
                {
                    this.m_pos--;
                    this.ThrowSyntaxError(SyntaxError.DuplicateFrom);
                }
                select.From = this.ScanTableName();
            }
            if (this.ScanToken(NodeType.Name, "where") != null)
            {
                if (select.Where != null)
                {
                    this.m_pos--;
                    this.ThrowSyntaxError(SyntaxError.DuplicateWhere);
                }
                select.Where = this.ScanWhere(select.From);
                goto Label_0023;
            }
            if (this.ScanToken(NodeType.Name, "group") != null)
            {
                this.EnsureToken(NodeType.Name, "by", SyntaxError.ExpectingBy);
                if (select.GroupBy != null)
                {
                    this.m_pos--;
                    this.ThrowSyntaxError(SyntaxError.DuplicateGroupBy);
                }
                select.GroupBy = this.ScanFieldNameList(false, NodeType.GroupFieldList);
                goto Label_0023;
            }
            if (this.ScanToken(NodeType.Name, "order") != null)
            {
                this.EnsureToken(NodeType.Name, "by", SyntaxError.ExpectingBy);
                if (select.OrderBy != null)
                {
                    this.m_pos--;
                    this.ThrowSyntaxError(SyntaxError.DuplicateOrderBy);
                }
                select.OrderBy = this.ScanFieldNameList(true, NodeType.OrderFieldList);
                goto Label_0023;
            }
            if (this.ScanToken(NodeType.Name, "asof") != null)
            {
                if (select.AsOf != null)
                {
                    this.m_pos--;
                    this.ThrowSyntaxError(SyntaxError.DuplicateAsOf);
                }
                select.AsOf = this.ScanValue(select.From);
                goto Label_0023;
            }
            select.StartOffset = node.StartOffset;
            select.EndOffset = ((NodeItem) this.m_tokens[this.m_pos - 1]).EndOffset;
            return select;
        }

        private Node ScanSingleValue(NodeTableName table)
        {
            int pos = this.m_pos;
            NodeItem item = this.NextToken();
            if ((item != null) && (((item.NodeType == NodeType.Number) || (item.NodeType == NodeType.String)) || (item.NodeType == NodeType.Variable)))
            {
                return item;
            }
            this.m_pos = pos;
            this.ThrowSyntaxError(SyntaxError.ExpectingValue);
            return null;
        }

        private NodeTableName ScanTableName()
        {
            int pos = this.m_pos;
            NodeItem item = this.NextToken();
            if ((item != null) && (item.NodeType == NodeType.Name))
            {
                NodeName n = (NodeName) item;
                if (((string.Compare(n.Value, "issue", StringComparison.OrdinalIgnoreCase) == 0) || (string.Compare(n.Value, "issues", StringComparison.OrdinalIgnoreCase) == 0)) || ((string.Compare(n.Value, "workitems", StringComparison.OrdinalIgnoreCase) == 0) || (string.Compare(n.Value, "workitem", StringComparison.OrdinalIgnoreCase) == 0)))
                {
                    n.Value = "WorkItem";
                }
                return new NodeTableName(n);
            }
            this.m_pos = pos;
            this.ThrowSyntaxError(SyntaxError.ExpectingTableName);
            return null;
        }

        private NodeItem ScanToken(NodeType type, string str)
        {
            int pos = this.m_pos;
            NodeItem item = this.NextToken();
            if (((item != null) && (item.NodeType == type)) && (string.Compare(item.Value, str, StringComparison.OrdinalIgnoreCase) == 0))
            {
                return item;
            }
            this.m_pos = pos;
            return null;
        }

        private Node ScanValue(NodeTableName table)
        {
            return this.ScanExpression(table);
        }

        private Node ScanValueList(NodeTableName table)
        {
            NodeValueList list = new NodeValueList();
            do
            {
                list.Add(this.ScanValue(table));
            }
            while (this.ScanToken(NodeType.Operation, ",") != null);
            list.StartOffset = list[0].StartOffset;
            list.EndOffset = list[list.Count - 1].EndOffset;
            return list;
        }

        private Node ScanWhere(NodeTableName table)
        {
            return this.ScanCondition4(table);
        }

        private void ThrowSyntaxError(SyntaxError err)
        {
            NodeItem node = (this.m_pos < this.m_tokens.Count) ? ((NodeItem) this.m_tokens[this.m_pos]) : null;
            throw new SyntaxException(node, err);
        }
    }
}

